package cn.sgjk.train.business.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.impl.BooleanConverter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.*;
import cn.hutool.log.Log;
import cn.sgjk.train.business.domain.*;
import cn.sgjk.train.business.dto.ConfirmOrderMQDto;
import cn.sgjk.train.business.enums.ConfirmOrderStatusEnum;
import cn.sgjk.train.business.enums.RedisKeyEnum;
import cn.sgjk.train.business.enums.SeatColEnum;
import cn.sgjk.train.business.enums.SeatTypeEnum;
import cn.sgjk.train.business.req.ConfirmOrderDoReq;
import cn.sgjk.train.business.req.ConfirmOrderTicketReq;
import cn.sgjk.train.common.context.LoginMemberContext;
import cn.sgjk.train.common.exception.BusinessException;
import cn.sgjk.train.common.exception.BusinessExceptionEnum;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import cn.sgjk.train.common.resp.PageResp;
import cn.sgjk.train.common.util.SnowUtil;
import cn.sgjk.train.business.mapper.ConfirmOrderMapper;
import cn.sgjk.train.business.req.ConfirmOrderQueryReq;
import cn.sgjk.train.business.req.ConfirmOrderSaveReq;
import cn.sgjk.train.business.resp.ConfirmOrderQueryResp;
import jakarta.annotation.Resource;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 确认订单服务
 *
 * @author sgjk
 * @date 2023/12/19
 */
@Service
public class ConfirmOrderService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfirmOrderService.class);


    @Resource
    private ConfirmOrderMapper confirmOrderMapper;

    @Autowired
    private DailyTrainTicketService dailyTrainTicketService;

    @Autowired
    private DailyTrainCarriageService dailyTrainCarriageService;

    @Autowired
    private DailyTrainSeatService dailyTrainSeatService;

    @Autowired
    private AfterConfirmOrderService afterConfirmOrderService;
    
    @Autowired
    private SkTokenService skTokenService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;




    public void save(ConfirmOrderSaveReq req) {
        DateTime now = DateTime.now();
        ConfirmOrder confirmOrder = BeanUtil.copyProperties(req, ConfirmOrder.class);
        if (ObjectUtil.isNull(confirmOrder.getId())) {
            confirmOrder.setId(SnowUtil.getSnowflakeNextId());
            confirmOrder.setCreateTime(now);
            confirmOrder.setUpdateTime(now);
            confirmOrderMapper.insert(confirmOrder);
        } else {
            confirmOrder.setUpdateTime(now);
            confirmOrderMapper.updateByPrimaryKey(confirmOrder);
        }
    }

    /**
     * 查询列表
     *
     * @param req
     * @return 页面响应＜确认订单查询响应＞
     */
    public PageResp<ConfirmOrderQueryResp> queryList(ConfirmOrderQueryReq req) {
        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
        confirmOrderExample.setOrderByClause("id desc");
        confirmOrderExample.createCriteria();

        LOG.info("查询页码：{}", req.getPage());
        LOG.info("每页条数：{}", req.getSize());
        PageHelper.startPage(req.getPage(), req.getSize());
        List<ConfirmOrder> confirmOrderList = confirmOrderMapper.selectByExample(confirmOrderExample);

        PageInfo<ConfirmOrder> pageInfo = new PageInfo<>(confirmOrderList);
        LOG.info("总行数：{}", pageInfo.getTotal());
        LOG.info("总页数：{}", pageInfo.getPages());

        List<ConfirmOrderQueryResp> list = BeanUtil.copyToList(confirmOrderList, ConfirmOrderQueryResp.class);

        PageResp<ConfirmOrderQueryResp> pageResp = new PageResp<>();
        pageResp.setTotal(pageInfo.getTotal());
        pageResp.setList(list);
        return pageResp;
    }

    /**
     *
     *
     * @param id
     */
    public void delete(Long id) {
        confirmOrderMapper.deleteByPrimaryKey(id);
    }

    /**
     *
     * @param confirmOrderMQDto
     */
    //@SentinelResource(value = "doComfire",blockHandler = "doComfireBlockExpection")
    public  void doComfire(ConfirmOrderMQDto confirmOrderMQDto) {
//        boolean getvalidSkToken = skTokenService.validSkToken(confirmOrderDoReq.getDate(),
//                confirmOrderDoReq.getTrainCode(),
//                LoginMemberContext.getId());
//
//        if(!getvalidSkToken) {
//            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_SK_TOKENB_FAIL);
//        }
        // 库存为 0 标识，减少后面线程访问数据库
        // 每日跑批成功后，从redis移除key


        // 分布式锁应该不放在生产者，放在消费者，跟购票跟流程紧
        RLock lock = null;
        // 将当前锁的粒度缩小到每个日期与每个车次上
        String lockName = RedisKeyEnum.CONFIRM_ORDER+"-"+
                DateUtil.format(confirmOrderMQDto.getDate(),"yyyy-mm-DD") +"-"+ confirmOrderMQDto.getTrainCode();

        // 省略业务数据校验，如：车次是否存在，余票是否存在，车次是否在有效期内，tickets条数>0，同乘客同车次是否已买过
        // 保存确认订单表，状态初始
        try {
            // 使用redission看门狗机制锁获取锁成功后，业务代码未执行自动续期
            lock = redissonClient.getLock(lockName);
            boolean tryLock = lock.tryLock(10,-1, TimeUnit.SECONDS);//leaseTime -1或者不设置参数，默认使用的是看门狗
            // 有可能第一次大量线程都判断还是有票，在等待锁之后，票卖完了，还是有线程进来访问数据库，因此再加一次判断

            if(tryLock) {
                LOG.info("恭喜，抢到锁");
//                for (int i = 0; i < 30; i++) {
//                    Long expire = redisTemplate.opsForValue().getOperations().getExpire(lockName);
//                    LOG.info("看门狗测试距离锁过期还有:{}",expire);
//                    Thread.sleep(1000);
//                }
            }else {
                LOG.info("没能抢到锁，有其他消费线程正在处理订单");
                return;
                //throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_LOCK_EXCEPTION);
            }

            // 由先抢到分布式锁的消费者处理所有同期车次初始化订单
            while (true) {
                // 取确认订单表的记录，同期车次，状态是I，分页处理(后端处理大量数据时，使用分页，避免一次性读到内存上)
                ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
                ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();
                criteria.andTrainCodeEqualTo(confirmOrderMQDto.getTrainCode())
                        .andDateEqualTo(confirmOrderMQDto.getDate())
                        .andStatusEqualTo(ConfirmOrderStatusEnum.INIT.getCode());
                PageHelper.startPage(1,5);
                List<ConfirmOrder> list = confirmOrderMapper.selectByExampleWithBLOBs(confirmOrderExample);

                if(CollUtil.isEmpty(list)) {
                    LOG.info("没有需要处理的订单，结束循环");
                    break;
                }else{
                    LOG.info("本次处理{}条订单",list.size());
                }
                // 一条一条卖
                list.forEach(confirmOrder -> {
                    try {
                        try {
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        sell(confirmOrder);
                        LOG.info("本订单完成，继续售卖下一个订单");
                    } catch (BusinessException e) {
                       if(e.getE().equals(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR)) {
                           // 这里处理失败下一订单，是因为当前订单没票，不代表同一车次不同站没票
                           LOG.info("本订单余票不足，继续售卖下一个订单");
                           confirmOrder.setStatus(ConfirmOrderStatusEnum.EMPTY.getCode());
                           updateStatus(confirmOrder);
                       }else{
                           throw e;
                       }
                    }
                });
            }
        } catch (Exception e) {

            e.printStackTrace();
        } finally {
            if(null != lock && lock.isHeldByCurrentThread()) {
                lock.unlock();
                LOG.info("购票流程结束，释放锁");
            }
        }

    }


    private void sell(ConfirmOrder confirmOrder) {
        ConfirmOrderDoReq confirmOrderDoReq = new ConfirmOrderDoReq();
        confirmOrderDoReq.setMemberId(confirmOrder.getMemberId());
        confirmOrderDoReq.setDate(confirmOrder.getDate());
        confirmOrderDoReq.setTrainCode(confirmOrder.getTrainCode());
        confirmOrderDoReq.setStart(confirmOrder.getStart());
        confirmOrderDoReq.setEnd(confirmOrder.getEnd());
        confirmOrderDoReq.setDailyTrainTicketId(confirmOrderDoReq.getDailyTrainTicketId());
        confirmOrderDoReq.setTickets(JSON.parseArray(confirmOrder.getTickets(),ConfirmOrderTicketReq.class));
        confirmOrderDoReq.setImageCode("");
        confirmOrderDoReq.setLogId("");
        confirmOrderDoReq.setImageCodeToken("");

        // 将订单设置成处理中，避免重复处理
        confirmOrder.setStatus(ConfirmOrderStatusEnum.PENDING.getCode());
        updateStatus(confirmOrder);
        LOG.info("将确认订单更新成处理中，避免重复处理,confirm_order:{}",confirmOrder.getStatus());


        Date date = confirmOrderDoReq.getDate();
        String trainCode = confirmOrderDoReq.getTrainCode();
        String start = confirmOrderDoReq.getStart();
        String end = confirmOrderDoReq.getEnd();
        List<ConfirmOrderTicketReq> tickets = confirmOrderDoReq.getTickets();

        // 从数据库查出订单
//        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
//        confirmOrderExample.setOrderByClause("id asc");
//        ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();
//        criteria.andDateEqualTo(date)
//                .andTrainCodeEqualTo(trainCode)
//                .andMemberIdEqualTo(confirmOrderDoReq.getMemberId())
//                .andStatusEqualTo(ConfirmOrderStatusEnum.INIT.getCode());
//        List<ConfirmOrder> list = confirmOrderMapper.selectByExampleWithBLOBs(confirmOrderExample);
//        ConfirmOrder confirmOrder;
//        if(CollUtil.isEmpty(list)) {
//            LOG.info("找不到原始订单,结束");
//            return;
//        }else{
//            LOG.info("本次处理{}条确认订单",list.size());
//            confirmOrder = list.get(0);
//        }

        // 查出余票记录，需要得到真实的库存
        DailyTrainTicket dailyTrainTicket = dailyTrainTicketService.selectByUnique(date, trainCode, start, end);



        // 扣减余票数量，并判断余票是否足够(预扣减，不更新到数据库)
        reduceTickets(confirmOrderDoReq,dailyTrainTicket);

        // 最终的选座结果： 存放当前乘车人已选择座位，避免多个乘客选同个座位
        List<DailyTrainSeat> finalSeatList = new ArrayList<>();

        // 计算相对第一个座位的偏移值
        // 比如选择的是C1,D2，则偏移值是: [0,5]
        // 比如选择的是A1,B1,C1，则偏移值是:[0,1,2]
        //先获取乘客下单的第一个乘客 查看seat是否有值，有值说明乘客是选座的
        ConfirmOrderTicketReq ticketReq0 = tickets.get(0);

        if(StrUtil.isNotBlank(ticketReq0.getSeat())) {
            LOG.info("本次购票有选座");
            String seatTypeCode = ticketReq0.getSeatTypeCode();
            //获取车座类型都有哪些列，用于计算所选座位与第一个座位的偏离值
            List<SeatColEnum> seatColEnums = SeatColEnum.getColsByType(seatTypeCode);
            LOG.info("本次选座的座位类型包括的列：{}",seatColEnums);

            //组成和前端两排选座一样的列表，用于作参照物的座位列表，例:{A1,C1,D1,F1,A2,B2,C2,F2}
            //先算出绝对偏移值，即在列表数组中的索引位置
            //例如 C1D2的绝对偏移值=[1,6]，减去第一位的值，最终得到相对偏移量[0,5]
            List<String> referSeatList = new ArrayList<>();
            for (int i = 1; i <= 2; i++) {
                for (SeatColEnum seatColEnum : seatColEnums) {
                    referSeatList.add(seatColEnum.getCode() + i);
                }
            }
            LOG.info("用于作参照物的座位列表：{}",referSeatList);

            //绝对偏移量，即：在参照座位列表中的位置
            List<Integer> absoluteOffsetList = new ArrayList<>();
            for (ConfirmOrderTicketReq ticket : tickets) {
                int index = referSeatList.indexOf(ticket.getSeat());
                absoluteOffsetList.add(index);
            }
            LOG.info("获取乘客座位列表绝对偏移量位置：{}",absoluteOffsetList);

            List<Integer> offsetList = new ArrayList<>();
            for (Integer index : absoluteOffsetList) {
                int offset = index - absoluteOffsetList.get(0);
                offsetList.add(offset);
            }
            LOG.info("获取乘客座位列表相对偏移量位置：{}",offsetList);

            /**
             *  seatTypeCode： 通过第一位乘客获取车座类型，只有在多个乘客选票同种票种才能选票，因此传入第一个位置的乘客即可
             *                选票不一致 就进入else分支逻辑
             *  ticketReq0.getSeat().split("")[0]： 从A1获取A  A1是参照物的座位 A是真实车位
             */

            // 选座
            getSeat(finalSeatList,
                    date,
                    trainCode,
                    seatTypeCode,
                    ticketReq0.getSeat().split("")[0],
                    offsetList,
                    dailyTrainTicket.getStartIndex(),
                    dailyTrainTicket.getEndIndex());
        }else{
            LOG.info("本次购票无选座");

            //  遍历乘客所选车座类型
            for (ConfirmOrderTicketReq ticket : tickets) {
                getSeat(finalSeatList,
                        date,
                        trainCode,
                        ticket.getSeatTypeCode(),
                        null,
                        null,
                        dailyTrainTicket.getStartIndex(),
                        dailyTrainTicket.getEndIndex());
            }
        }
        // 选中座位后的事务处理：
        // 座位表修改售卖情况sell；
        // 余票详情表修改余票；
        // 为会员增加购票记录
        // 更新确认订单为成功
        LOG.info("最终选票:{}",finalSeatList);
        try {
            afterConfirmOrderService.AfterDoComfire(dailyTrainTicket,finalSeatList,tickets,confirmOrder);
        } catch (Exception e) {
            LOG.error("保存购票信息失败",e);
            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_EXCEPTION);
        }
        // 删缓存
        removeCache(confirmOrderDoReq.getDate(),confirmOrderDoReq.getTrainCode(), start, end);
    }

    private void updateStatus(ConfirmOrder confirmOrder) {
        ConfirmOrder confirmOrderForUpdate = new ConfirmOrder();
        confirmOrderForUpdate.setId(confirmOrder.getId());
        confirmOrderForUpdate.setUpdateTime(new Date());
        confirmOrderForUpdate.setStatus(confirmOrder.getStatus());
        confirmOrderMapper.updateByPrimaryKeySelective(confirmOrderForUpdate);
    }


    /**
     *
     * @param date       日期
     * @param trainCode  列车代码
     * @param seatType   座椅类型
     * @param colume     类型
     * @param offsetList 偏移列表
     */
    private void getSeat(List<DailyTrainSeat> finalSeatList,
                         Date date, String trainCode, String seatType,
                         String colume,List<Integer> offsetList,
                         Integer startIndex, Integer endIndex) {
        List<DailyTrainCarriage> carriageList =
                dailyTrainCarriageService.selectBySeatType(date,trainCode,seatType);
        //创建个临时变量，存放乘客成功选座座位信息
        List<DailyTrainSeat> getSeatList = new ArrayList<>();
        LOG.info("共有{}个符合条件的车厢",carriageList.size());

        // 一个车箱一个车箱的获取座位数据
        for (DailyTrainCarriage carriage : carriageList) {

            // 当选座不是同一个车厢 清空未完整完成选座的临时变量
            getSeatList = new ArrayList<>();

            LOG.info("开始从{}车厢选座",carriage.getIndex());
            List<DailyTrainSeat> seatList =
                    dailyTrainSeatService.selectByCarriage(date,trainCode,carriage.getIndex(),seatType);
            LOG.info("车厢的座位数:{}",seatList.size());

            for (DailyTrainSeat dailyTrainSeat : seatList) {


                //判断当前车座不能被选中(针对当前用户多个乘客)
                boolean alreadyChooseFlag = false;
                for (DailyTrainSeat finalSeat : finalSeatList) {
                    if(finalSeat.getId().equals(dailyTrainSeat.getId())) {
                        //当前车座已被选中
                        alreadyChooseFlag = true;
                        break;
                    }
                }

                if(alreadyChooseFlag) {
                    LOG.info("座位{}被选中过，不能重复选中，继续判断下一个座位",dailyTrainSeat.getCarriageSeatIndex());
                    continue;
                }


                // 判断colume是否有值，有值是选座的，再需要判断列号
                String col = dailyTrainSeat.getCol();
                Integer seatIndex = dailyTrainSeat.getCarriageSeatIndex();
                if (StrUtil.isBlank(colume)) {
                    LOG.info("无选座");
                }else{
                    if(!colume.equals(col)) {
                        // 列号比对不上再继续一次循环
                         LOG.info("座位{}列值不对，继续判断下一个座位，当前列值:{},目标列值{}",
                                 seatIndex,col,colume);
                        continue;
                    }
                }

                // 选座：第一个座位 && 无选座
                boolean isChoose = calSell(dailyTrainSeat, startIndex, endIndex);
                if(isChoose) {
                    LOG.info("选中座位");
                    getSeatList.add(dailyTrainSeat);
                }else {
                    continue;
                }


                // 根据offset选剩下的座位
                // isGetAllOffsentSeat： 当超出车厢退出当前的车厢里的车座循环
                boolean isGetAllOffsentSeat = true;
                if(CollUtil.isNotEmpty(offsetList)) {
                    LOG.info("有偏移值：{}，校验偏移的座位是否可选",offsetList);
                    // 从索引1开始， 索引0就是当前已选中的票
                    for (int i = 1; i < offsetList.size(); i++) {
                        Integer offset = offsetList.get(i);
                        // seatIndex: 库里座位的索引   offset: 下一个目标的索引
                        int nextIndex = seatIndex + offset - 1;
                        LOG.info("下一个索引的值:{},以及座位列号:{}",nextIndex,seatList.get(nextIndex).getCol());

                        //有选座时，一定是在同一个车厢
                        if(nextIndex >=  seatList.size()) {
                            LOG.info("座位{}不可选，偏移后的索引超出了这个车厢的座位数");
                            isGetAllOffsentSeat = false;
                            break;
                        }

                        DailyTrainSeat nextDailyTrainSeat = seatList.get(nextIndex);
                        boolean isChooseNext = calSell(nextDailyTrainSeat, startIndex, endIndex);
                        if (isChooseNext) {
                            LOG.info("座位{}被选中",nextDailyTrainSeat.getCarriageSeatIndex());
                            finalSeatList.add(nextDailyTrainSeat);
                            getSeatList.add(dailyTrainSeat);
                        }else{
                            LOG.info("座位{}不可选",nextDailyTrainSeat.getCarriageSeatIndex());
                            isGetAllOffsentSeat = false;
                            break;
                        }
                    }
                }
                if(!isGetAllOffsentSeat) {
                    getSeatList = new ArrayList<>();
                    continue;
                }
                //保存好的座位

                //临时变量放到最终选座列表里
                finalSeatList.addAll(getSeatList);
                return;
            }

        }
    }

    /**
     *  计算某座位在区间内是否可卖
     *  例: sell：10001, 本次购买区间站 1~4,则区间已售000  000:未售
     *  全部是0，表示这个区间可买;只要有1,就表示区间内已售过票
     *
     *  选中后，要计算购票后的sell,比如原来是10001 ,本次购买区间是1~4
     *  方案： 构造本次购票造成的售卖信息 01110,和原来 sell 10001按位或，最终得到 11111
     */

    private boolean calSell(DailyTrainSeat dailyTrainSeat, Integer startIndex, Integer endIndex) {
        // 00001  00000
        String sell = dailyTrainSeat.getSell();
        // 000   000  sellPart是通过站与站截取出来的区间
        String sellPart = sell.substring(startIndex, endIndex);
        if (Integer.parseInt(sellPart) > 0) {
            LOG.info("座位{}在本次车站区间{}~{}已售过票，不可选中该座位",
                    dailyTrainSeat.getCarriageSeatIndex(),startIndex,endIndex);
            return false;
        }else{
            LOG.info("座位{}在本次车站区间{}~{}未售过票，选中该座位",
                    dailyTrainSeat.getCarriageSeatIndex(),startIndex,endIndex);
            // 111  111 构建卖出去的区间  第一站是0
            String curSell = sellPart.replace('0', '1');
            // 0111  0111   endIndex长度 = 已售出区间 + 前面一个位的长度
            curSell = StrUtil.fillBefore(curSell, '0', endIndex);
            // 01110  01110
            curSell = StrUtil.fillAfter(curSell,'0',sell.length());

            // 当前区间售票信息curSell 01110与库里的已售sell 00001按位或，即可得该座位卖出此票后的售票详情
            // 15(01111),   14(01110 = 01110 | 00000)
            int newSellInt = NumberUtil.binaryToInt(curSell) | NumberUtil.binaryToInt(sell);
            // 1111
            String newSell = NumberUtil.getBinaryStr(newSellInt);
            //二进制会忽略高位 0 例 15 01111 为赋值为 1111 因此通过填充sell高位补0
            newSell = StrUtil.fillBefore(newSell,'0',sell.length());
            LOG.info("座位{}被选中，原售票信息:{},车站区间{}~{},既:{},最终售票信息:{}"
            ,dailyTrainSeat.getCarriageSeatIndex(),sell,startIndex,endIndex,curSell,newSell);
            dailyTrainSeat.setSell(newSell);

            return true;
        }
    }


    /**
     * @param confirmOrderDoReq 确认订单是否需要
     * @param dailyTrainTicket  每日火车票
     */
    private void reduceTickets(ConfirmOrderDoReq confirmOrderDoReq, DailyTrainTicket dailyTrainTicket) {
        for (ConfirmOrderTicketReq ticketReq0 : confirmOrderDoReq.getTickets()) {
            String seatTypeCode = ticketReq0.getSeatTypeCode();
            SeatTypeEnum seatTypeEuem = EnumUtil.getBy(SeatTypeEnum::getCode, seatTypeCode);
            //获取一个对象的不固定的属性，就会用到反射，反射不如直接写分支判断
            switch (seatTypeEuem) {
                case YDZ -> {
                    int countLeft = dailyTrainTicket.getYdz() - 1;
                    if(countLeft < 0) {

                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYdz(countLeft);
                }
                case EDZ -> {
                    int countLeft = dailyTrainTicket.getEdz() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setEdz(countLeft);
                }
                case YW -> {
                    int countLeft = dailyTrainTicket.getYw() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYw(countLeft);
                }
                case RW -> {
                    int countLeft = dailyTrainTicket.getRw() - 1;
                    if(countLeft < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setRw(countLeft);
                }
            }
        }
    }

    //下单成功或生成每日车次余票生成成功后也清除页面缓存
    public void removeCache(Date date,String trainCode, String start, String end) {
        String ticketDate = ":" + DateUtil.format(date, "yyyy-MM-dd");
        String trainCodeCache = ":" + trainCode;
        String TICKET_KEY = "DailyTrainTicketService.queryList"+ ticketDate + trainCodeCache;
        String TICKET_KEY_ORDER = "DailyTrainTicketService.queryList"+ ticketDate + ":" + start + ":"+ end;

        Set<String> keys = redisTemplate.keys(TICKET_KEY + "*");
        Set<String> orderKeys = redisTemplate.keys(TICKET_KEY_ORDER + "*");

        Set<String> allKeys = new HashSet<>();
        allKeys.addAll(keys);
        allKeys.addAll(orderKeys);
        allKeys.stream().forEach(str -> {
            LOG.info("redis目前有{}的缓存",str);
        });
        redisTemplate.delete(allKeys);
    }

    private boolean isNotRemian(String isNotTicketKey) {

        Object value = redisTemplate.opsForValue().get(isNotTicketKey);
        if(ObjectUtil.isNotNull(value)) {
            boolean isNotRemain = BooleanUtil.toBoolean(value.toString());
            LOG.info("余票标识:{}",isNotRemain);
            // 当票标识为卖完,不往下执行
            if(isNotRemain) {
                LOG.info("票卖完了");
                return true;

            }
        }
        return false;
    }

    /**
     *  查询前面有几个人在排队
     */

    public Integer queryLineCount(Long id) {
        ConfirmOrder confirmOrder = confirmOrderMapper.selectByPrimaryKey(id);
        // 根据订单状态获取枚举
        ConfirmOrderStatusEnum statusEnum = EnumUtil.getBy(ConfirmOrderStatusEnum::getCode, confirmOrder.getStatus());
        int result = switch (statusEnum) {
            case PENDING -> 0;   //排队 0
            case SUCCESS -> -1;   //成功
            case FAILURE -> -2;   //失败
            case EMPTY -> -3;   //无票
            case CANCEL -> -4;   //排队 0
            case INIT -> 999;   // 需要查表得到实际排队数量
        };
        if(result == 999) {
            // 排在第几位 where a = 1 and (b=1 or c=1) 等价于 where a = 1 and b=1 or a=1 and c=1
            ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
            confirmOrderExample.or().andTrainCodeEqualTo(confirmOrder.getTrainCode())
                    .andDateEqualTo(confirmOrder.getDate())
                    .andCreateTimeLessThan(confirmOrder.getCreateTime())
                    .andStatusEqualTo(ConfirmOrderStatusEnum.PENDING.getCode());
            confirmOrderExample.or().andTrainCodeEqualTo(confirmOrder.getTrainCode())
                    .andDateEqualTo(confirmOrder.getDate())
                    .andCreateTimeLessThan(confirmOrder.getCreateTime())
                    .andStatusEqualTo(ConfirmOrderStatusEnum.INIT.getCode());
            return Math.toIntExact(confirmOrderMapper.countByExample(confirmOrderExample));
        }else {
            return result;
        }
    }

    private void doComfireBlockExpection(ConfirmOrderDoReq confirmOrderDoReq, BlockException e) {
        LOG.info("当前购票被限流:{}",e);
        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FOLW_EXCEPTION);
    }



}
